﻿/*
 * Created by SharpDevelop.
 * User: Emanuel Santos
 * Date: 24-05-2013
 * Time: 09:13
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Net.Sockets;
using System.Text;
using DLGeneric;
using System.Net;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Leiloes;


namespace Server
{
    /// <summary>
    /// Description of ConnectionThread.
    /// </summary>
    public class ConnectionThread
    {
        private static int connections_ = 0;
        private int qtBytes;
        private byte[] buffer = new byte[1024];
        private byte[] bufferRes = new byte[1024];
        private ArrayList pacote = new ArrayList();
        private IEnumerator lista;

        //dialer
        private Socket dialer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private IPEndPoint ie = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 7000);
        //dialer

        private DLLeilao bdL = new DLLeilao();
        //private DLContabilidade bdF = new DLContabilidade();

        public bool sessaoFechada = false;
        public Socket threadClient;
        string id = "";

        public int connections
        {
            get
            {
                return connections_;
            }
        }

        public ConnectionThread()
        {
        }

        /// <summary>
        /// Metodo resposável por gerir as ligações com os clientes
        /// </summary>
        public void HandleConnection()
        {

            Console.WriteLine("Cliente: {0} conectado, ligação nr: {1}", threadClient.RemoteEndPoint.ToString(), ++connections_);
            try
            {
                while (true)
                {
                    qtBytes = threadClient.Receive(buffer); //Indication
                    lista = abrePacote(buffer);
                    lista.MoveNext();
                    string dataStr = lista.Current.ToString();
                    string erro = "";
                    switch (dataStr)
                    {
                        case "AUTH.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            Console.WriteLine("AUTH.RES");
                            auth_res();
                            break;

                        case "VSALDO.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            vsaldo_req();
                            break;
                        case "ADEPOS.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            depos_req();
                            break;
                        case "ALEILAO.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            aleilao_req();
                            break;
                        case "LLATIV.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            llAtiv_req();
                            break;
                        case "LLDISP.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            llDisp_req();
                            break;
                        case "DLEILAO.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            dLeilao_req();
                            break;
                        case "LICITAR.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            licitar_req();
                            break;
                        case "NOTIC.REQ":
                            Console.WriteLine(" {0} ", dataStr);
                            notif_req();
                            break;
}

                    if (dataStr == "exit" || erro == "1")
                    {
                        break;
                    }
                }
            }
            catch (SocketException ex)
            {
                connections_--;   
            }
            catch (Exception e)
            {
                Console.WriteLine("Erro nas ligações" + e.GetType());
            }
        }

        /// <summary>
        /// Reencaminha as respostas que vêm do dialer para os metodos correspondentes
        /// </summary>
        /// <param name="bufer"></param>
        public void fowardFromDialer(byte[] bufer)
        {
            lista = abrePacote(bufer);
            lista.MoveNext();
            switch (lista.Current.ToString())
            {
                case "VSALDO.RES":
                    vsaldo_res(bufer);
                    break;
                case "ADEPOSSC.RES":
                    depos_res(bufer);
                    break;
                case "VATIVOS.RES":
                    vativos_res(bufer);
                    break;
                case "LICITAR.RES":
                    licitar_res(bufer);
                    break;
            }
        }

        /// <summary>
        /// Metodo de resposta à autenticação. Recebe o user e password cifrada do cliente
        /// descifra verifica na bd e devolve o id ao cliente em caso sucesso.
        /// </summary>
        public void auth_res()
        {
            string id;
            string erro;
            lista.MoveNext();
            string user = lista.Current.ToString();
            lista.MoveNext();
            string pass = Cifra.Cifragem.deCifrar(lista.Current.ToString(), user);
            id = bdL.Autenticar(user, pass);
            if (id != "")
            {
                pacote.Clear();
                pacote.Add("AUTH.RES");
                pacote.Add("true");
                pacote.Add(id);
                threadClient.Send(criaPacote(pacote)); // RESPONSE
            }
            else
            {
                pacote.Clear();
                pacote.Add("AUTH.RES");
                pacote.Add("false");
                threadClient.Send(criaPacote(pacote)); // RESPONSE

                qtBytes = threadClient.Receive(buffer); //Indication
                erro = Encoding.ASCII.GetString(buffer).Substring(0, qtBytes);
            }
        }

        /// <summary>
        /// Adicionar novo leilão
        /// </summary>
        public void aleilao_req()
        {
            lista.MoveNext();
            Leilao aleilao =(Leilao)lista.Current;
            pacote.Clear();
            pacote.Add(bdL.adicionarLeiao(aleilao));
            Console.WriteLine("ALEILAO.RES");
            threadClient.Send(criaPacote(pacote));

        }

        /// <summary>
        /// Listar Leilões disponíveis
        /// </summary>
        public void llDisp_req()
        {
            lista.MoveNext();
            pacote.Clear();
            Console.WriteLine("LLDISP.RES");
            threadClient.Send(criaPacote(bdL.verLeiloesDisponiveis(int.Parse(lista.Current.ToString()))));
        }

        /// <summary>
        /// Detalhes leilão
        /// </summary>
        public void dLeilao_req()
        {
            lista.MoveNext();
            pacote.Clear();
            Console.WriteLine("DLEILAO.RES");
            pacote.Add(bdL.leilaoDetalhe(int.Parse(lista.Current.ToString())));
            threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Licitar num novo leilão
        /// Pedido
        /// </summary>
        public void licitar_req()
        {
            lista.MoveNext();
            id = lista.Current.ToString();
            lista.MoveNext();
            double valor = double.Parse(lista.Current.ToString());
            lista.MoveNext();
            int idLeilao = int.Parse(lista.Current.ToString());
            
            pacote.Clear();
            pacote.Add("LICITAR.REQ");
            pacote.Add(id);
            pacote.Add(valor);
            pacote.Add(idLeilao);
            dialFinancial(criaPacote(pacote));
            bdL.adicionarCompradorLeilao(idLeilao, int.Parse(id), valor);
            //threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Licitar num novo leilão
        /// Enviar Reposta
        /// </summary>
        /// <param name="bufer"></param>
        public void licitar_res(byte[] bufer)
        {
            Console.WriteLine("LICITAR.RES");
            lista = abrePacote(bufer);
            lista.MoveNext();
            lista.MoveNext();
            pacote.Clear();
            pacote.Add(lista.Current);
            threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Request Ver Saldo. Faz uma chamada ao servidor Financeiro com o Id do user.
        /// </summary>
        public void vsaldo_req()
        {
            lista.MoveNext();
            id = lista.Current.ToString();
            pacote.Clear();
            pacote.Add("VSALDO.REQ");
            pacote.Add(id);
            dialFinancial(criaPacote(pacote));

        }

        /// <summary>
        /// Response Ver saldo. Recebe do servidor financeiro um buffer com os dados e reencaminha para o cliente o saldo
        /// </summary>
        /// <param name="bufer"></param>
        public void vsaldo_res(byte[] bufer)
        {
            Console.WriteLine("VSALDO.RES");
            lista = abrePacote(bufer);
            lista.MoveNext();
            lista.MoveNext();
            pacote.Clear();
            pacote.Add(lista.Current.ToString());
            threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Request depositar. Faz uma chamada ao servidor Financeiro com o Id do user e o valor a depositar.
        /// </summary>
        public void depos_req()
        {
            lista.MoveNext();
            id = lista.Current.ToString();
            lista.MoveNext();
            pacote.Clear();
            pacote.Add("ADEPOSSC.REQ");
            pacote.Add(id);
            pacote.Add(lista.Current.ToString());
            Console.WriteLine("ADEPOSSC.REQ");
            dialFinancial(criaPacote(pacote));
        }

        /// <summary>
        /// Response depositar. Recebe do servidor Financeiro o novo valor na conta e reencaminha ao cliente.
        /// </summary>
        public void depos_res(byte[] bufer)
        {
            Console.WriteLine("ADEPOSSC.RES");
            Console.WriteLine("ADEPOS.RES");
            lista = abrePacote(bufer);
            lista.MoveNext();
            lista.MoveNext();
            pacote.Clear();
            pacote.Add(lista.Current.ToString());
            threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Request Ver ativos. Faz uma chamada ao servidor Financeiro com o Id do user.
        /// </summary>
        public void llAtiv_req()
        {
            lista.MoveNext();
            pacote.Clear();
            Console.WriteLine("LLATIV.RES");
            threadClient.Send(criaPacote(bdL.verLeiloesAtivos(int.Parse(lista.Current.ToString()))));
        }

        /// <summary>
        /// Response ver ativos. Recebe do servidor Financeiro os leilões ativos do cliente e reecaminha para o mesmo.
        /// </summary>
        public void vativos_res(byte[] bufer)
        {
            Console.WriteLine("VATIVOS.RES");
            lista = abrePacote(bufer);
            lista.MoveNext();
            lista.MoveNext();
            pacote.Clear();
            pacote.Add(lista.Current.ToString());
            threadClient.Send(criaPacote(pacote));
        }

        /// <summary>
        /// Notificar cliente
        /// </summary>
        public void notif_req()
        {
            Notificar not = null;
            lista.MoveNext(); 
            pacote.Clear();
            pacote = bdL.verificarNotificaoesPendentes(int.Parse(lista.Current.ToString()));
            bdL.marcarComoNotificado(int.Parse(lista.Current.ToString()));
            lista = pacote.GetEnumerator();
            while (lista.MoveNext())
            {
                not = (Notificar)lista.Current;
                if (not.tipo == "vendedor")
                {
                    if (not.sucesso) NotificarEmail.Instance.enviar(not.email,"Nova notificação do sistema de leilões", "Caro vendedor: "+not.nome +"<br> O seu leilão terminou com licitações no artigo: " + not.titulo+ " com o valor final de: " + not.valorfinal.ToString());
                    else 
                    {
                        NotificarEmail.Instance.enviar(not.email,"Nova notificação do sistema de leilões", "Caro vendedor: "+not.nome +"<br>O seu leilão terminou sem licitações!");
                    }
                }
                else 
                {
                        NotificarEmail.Instance.enviar(not.email, "Nova notificação do sistema de leilões", "Caro utilizador: " + not.nome + "<br>O seu leilão terminou sem licitações!");
                }
            }
            Console.WriteLine("NOTIC.RES");
            threadClient.Send(criaPacote(pacote));
            
        }

        /// <summary>
        /// Recebe o buffer a enviar. Estabelece a conecção ao servidor Financeiro e recebe a resposta em lista.
        /// </summary>
        /// <param name="bufer"></param>
        /// <returns> lista iEnumerator</returns>
        public void dialFinancial(Byte[] bufer)
        {
            try
            {
                if (dialer.Connected == false)
                {
                    dialer.Connect(ie);
                    sessaoFechada = false;
                }
                dialer.Send(bufer);

                qtBytes = dialer.Receive(buffer);
                fowardFromDialer(buffer);
                //dialer.Disconnect(true); //TESTAR O DISCONNECT!!!
            }
            catch (Exception e)
            {
                
                Console.WriteLine("Falha comunicação no dialer . . . " + e.ToString());
                dialer.Close();
                sessaoFechada = true;
                Console.ReadKey();
            }
        }

        /// <summary>
        /// Arraylist->Stream->Serialize->byte[]
        /// </summary>
        /// <param name="pacote"></param>
        /// <returns>Byte[] para ser enviado pelo socket</returns>
        public byte[] criaPacote(ArrayList pacote)
        {
            MemoryStream fs = new MemoryStream();
            //fs.Position = 0;

            BinaryFormatter formatter = new BinaryFormatter();

            formatter.Serialize(fs, pacote);

            byte[] bufer = fs.ToArray();
            //fs.Flush();
            return bufer;
        }

        /// <summary>
        /// byte[]->stream->deserialize->arraylist->enumerator
        /// </summary>
        /// <param name="bufer"></param>
        /// <returns>Devolve o enumerator do pacote recebido</returns>
        public IEnumerator abrePacote(Byte[] bufer)
        {
            MemoryStream ms = new MemoryStream(bufer);
           // ms.Position = 0;

            BinaryFormatter formatter = new BinaryFormatter();

            ArrayList pacote = (ArrayList)formatter.Deserialize(ms);
            //ms.Flush();
            return pacote.GetEnumerator();
        }
    }
}
